home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fatted Calf
/
The Fatted Calf.iso
/
Applications
/
Publishing
/
ImagePortfolio
/
Source
/
Portfolio.m
< prev
Wrap
Text File
|
1994-04-01
|
28KB
|
998 lines
// -------------------------------------------------------------------------------------
// Portfolio.m - image Palette instance manager
// Martin D. Flynn, NeXT Computer, Inc.
// You may freely copy, distribute and reuse the code in this example.
// NeXT disclaims any warranty of any kind, expressed or implied, as to its
// fitness for any particular use.
// -------------------------------------------------------------------------------------
#import <stdlib.h>
#import <stdio.h>
#import <string.h>
#import <math.h>
#import <libc.h>
#import <sys/types.h>
#import <sys/stat.h>
#import <sys/time.h>
#import <defaults/defaults.h>
#import <dpsclient/psops.h>
#import <dpsclient/dpsNeXT.h>
#import <objc/objc-runtime.h>
#import <objc/NXBundle.h>
#import <appkit/graphics.h>
#import <appkit/Button.h>
#import <appkit/Panel.h>
#import <appkit/Matrix.h>
#import <appkit/ScrollView.h>
#import <appkit/NXCursor.h>
#import <appkit/NXImage.h>
#import <appkit/NXBitmapImageRep.h>
#import <appkit/NXEPSImageRep.h>
#import <appkit/SavePanel.h>
#import <appkit/FontManager.h>
#import "ExpandedView.h"
#import "PaletteCell.h"
#import "PaletteMatrix.h"
#import "Portfolio.h"
#import "ImagePortfolio.h"
// -------------------------------------------------------------------------------------
/* static image ids */
static id openFolder = (id)nil;
static id closedFolder = (id)nil;
static id multiFile = (id)nil;
static id stopIcon = (id)nil;
/* list of instantiated windows */
static id instanceList = (id)nil;
/* main window location scale */
static NXPoint winPosScale = { 0.20, 0.85 };
static NXSize winOffset = { 64.0, -24.0 };
// -------------------------------------------------------------------------------------
// misc defines
#define isLOADING ([iconButtonId altImage] == stopIcon)
#define dftCellROWS 1
#define dftCellCOLUMNS 5
#define dftFormatNAME "PortfolioFormat"
#define X origin.x
#define Y origin.y
#define W size.width
#define H size.height
// -------------------------------------------------------------------------------------
@implementation Portfolio
// -------------------------------------------------------------------------------------
// internal support
/* return first instance */
+ firstInstance
{
return (instanceList && [instanceList count])? [instanceList objectAt:0]: (id)nil;
}
// -------------------------------------------------------------------------------------
// internal support
/* set icon button image */
- _setIcon:imageId
{
if (imageId) [iconButtonId setAltImage:imageId];
[iconButtonId setImage:[iconButtonId altImage]];
return self;
}
/* set window title from file name */
- _setWindowTitle:(char*)fileName
{
char buff[MAXPATHLEN + 1], *name;
/* set last file path */
if (fileName) {
[NXApp setLastPathNoCopy:filePATH(fileName)];
name = fileName;
} else {
int len = strlen(strcpy(buff, [NXApp lastPath]));
strcat(buff, ((!len||(buff[len-1]!='/'))?"/UNTITLED":"UNTITLED"));
name = buff;
}
/* change window title and add to windows menu */
[paletteWindow setTitleAsFilename:name];
return self;
}
/* make save panel */
- _savePanel:(char*)title :(char*)extn
{
id savePanel = [SavePanel new];
[savePanel setTitle:title];
[savePanel setPrompt:"File:"];
[savePanel setRequiredFileType:extn];
[savePanel setDirectory:[NXApp lastPath]];
return savePanel;
}
/* (re)adjust window size to fit cell size */
- _adjustPreference:(char*)prefBuff
{
int rows, cols;
NXSize winSize, *cellSize, *gap;
NXRect fRect;
BOOL rtn;
/* free preference buffer and check for successful */
rtn = [iconMatrix setPreferences:prefBuff returnRows:&rows cols:&cols];
free(prefBuff);
if (!rtn) return self;
/* calculate minimum window size */
cellSize = [iconMatrix cellSize];
gap = [iconMatrix intercell];
[self getWindowSize:&winSize forCellSize:cellSize gap:gap rows:rows cols:cols];
[paletteWindow getFrame:&fRect];
fRect.Y += fRect.H - winSize.height;
fRect.size = winSize;
/* resize and re-display window */
[paletteWindow placeWindow:&fRect];
[self windowDidResize:paletteWindow];
[paletteWindow display];
[paletteWindow makeKeyAndOrderFront:(id)nil];
return self;
}
/* (load thread) recursive add directory to image list */
- _addDirectory:(char*)dirPath :(BOOL)chkExtn
{
struct stat statBuf;
if (abortLoad) return (id)nil;
if (!dirPath || stat(dirPath, &statBuf)) return (id)nil;
if (statBuf.st_mode & S_IFDIR) sendDIRECTORY(dirPath, self, _cmd, (id)((int)chkExtn));
else {
if (chkExtn && !_validExtension(dirPath)) return (id)nil;
[iconMatrix addImage:dirPath];
}
return self;
}
/* (load thread) load from doc file */
- _loadDocument:(char*)fileName :(BOOL)doPref :(BOOL)checkExtension
{
int p;
char name[MAXPATHLEN + 1], *ptr;
FILE *fNum;
/* return if no file name, or if file can't be openned */
if (!fileName || !(fNum=fopen(fileName,"r"))) return (id)nil;
/* read image paths from file */
for (*name = 0; !abortLoad && fgets(name, sizeof(name) - 1, fNum);) {
if (name[(p = strlen(name) - 1)] == '\n') name[p] = 0;
if (*name != ':') { [self _addDirectory:name :checkExtension]; continue; }
if (doPref) {
ptr = NXCopyStringBuffer(name + 1);
[self mainThreadPerform:@selector(_adjustPreference:) with:(id)ptr wait:YES];
}
}
fclose(fNum);
/* tell matrix to resize */
[iconMatrix mainThreadPerform:@selector(sizeToCells) wait:NO];
return self;
}
/* (load thread) load file list */
- _doLoad:(fileLIST*)fileList
{
char *s, *d, filePath[MAXPATHLEN + 1];
fileLIST *fl, *next;
/* return if no list */
if (!fileList) return self;
/* initial load lock */
for (fl = fileList; fl;) {
/* continue loading if not stopped */
if (!abortLoad) {
BOOL quit, chkExt, openDoc, setEdit;
/* load list of images (separated by tabs) */
chkExt = (fl->flags & 2)?YES:NO;
openDoc = (fl->flags & 1)?YES:NO;
setEdit = (fl->flags & 1)?NO:YES;
for (quit = NO, s = fl->list; !abortLoad && !quit && *s;) {
for (d = filePath; (*d = *s++) && (*d != '\t'); d++); // copy til tab or nil
if (!*d) quit = YES; else *d = 0; // check end-of-list
if (strcmp(fileEXT(filePath)+1, docEXTENSION))
[self _addDirectory:filePath :chkExt];
else
[self _loadDocument:filePath :openDoc :NO];
}
/* set window edited icon (setEdit only true on the first fileLIST loaded) */
[paletteWindow mainThreadPerform:@selector(setDocEdited:)
with:(id)((int)setEdit) wait:NO];
}
/* free list and move to next list of images */
mutex_lock(loadMutex);
next = fl->next;
free(fl->list);
free(fl);
fl = next;
if (!fl) loadList = (fileLIST*)nil;
mutex_unlock(loadMutex);
}
/* loading complete */
[self mainThreadPerform:@selector(_loadComplete) wait:NO];
return self;
}
/* invoked by load thread when loading is complete */
- _loadComplete
{
mutex_lock(loadMutex);
if (!loadList) { // make sure another load hasn't been requested
abortLoad = YES;
[self _setIcon:closedFolder];
[iconMatrix resizeAndDisplay];
}
mutex_unlock(loadMutex);
return self;
}
// -------------------------------------------------------------------------------------
// create a new instance
/* general initialization */
- init
{
return [self initFromFile:(char*)nil];
}
/* initialize and open file name */
- initFromFile:(const char*)fileName
{
return [self initFromFile:fileName registerWindow:YES];
}
/* initialize and open file name */
- initFromFile:(const char*)fileName registerWindow:(BOOL)regWindow
{
id docView;
int rows, cols;
const char *dftFmt;
NXSize cellSize, scrnSize, gap, clipSize;
NXRect docFrame, winRect;
static int winCount = 0;
/* initialize static vars */
if (!openFolder ) openFolder = [NXImage findImageNamed:"openFolder" ];
if (!closedFolder) closedFolder = [NXImage findImageNamed:"closedFolder"];
if (!multiFile ) multiFile = [NXImage findImageNamed:"multiFile" ];
if (!stopIcon ) stopIcon = [NXImage findImageNamed:"stop" ];
if (!instanceList) instanceList = listALLOC(1);
/* init super and add myself to the list of active instances */
[super init];
[instanceList addObject:self];
[self resignActivePortfolio:self];
/* load nib */
[NXApp loadNibSection:"Portfolio.nib" owner:self];
/* set icon button to send message on mouse down */
fileIcon = (id)nil;
[iconButtonId sendActionOn:NX_MOUSEDOWNMASK];
[self _setIcon:closedFolder];
/* file name load list flags */
abortLoad = YES;
loadMutex = mutex_alloc();
loadList = (fileLIST*)nil;
/* make a new matrix */
docView = [paletteScroll docView];
[docView getFrame:&docFrame];
[docView getCellSize:&cellSize];
iconMatrix = [[PaletteMatrix alloc] initFrame:&docFrame];
[iconMatrix setCellSize:&cellSize];
[iconMatrix setFont:[[docView cellAt:0:0] font]];
[iconMatrix setDelegate:self];
[iconMatrix setTarget:self];
[iconMatrix setDoubleAction:@selector(showLargeImage:)];
[paletteScroll setDocView:iconMatrix];
[paletteScroll setPageScroll:0.0]; // full page scrolling
[[paletteScroll setHorizScroller:(id)nil] free]; // remove the horiz scroller
[docView free]; // free the old view
/* defaults */
dftFmt = NXReadDefault([NXApp appName], dftFormatNAME);
if (![iconMatrix setPreferences:(char*)dftFmt returnRows:&rows cols:&cols]) {
rows = dftCellROWS;
cols = dftCellCOLUMNS;
}
/* calculate new window size and location */
cellSize = *[iconMatrix cellSize]; // re-read cellSize
gap = *[iconMatrix intercell];
[NXApp getScreenSize:&scrnSize];
[self getWindowSize:&winRect.size forCellSize:&cellSize gap:&gap rows:rows cols:cols];
winRect.X = floor(scrnSize.width *winPosScale.x)+((float)winCount*winOffset.width );
winRect.Y = floor(scrnSize.height*winPosScale.y)+((float)winCount*winOffset.height);
if (winRect.X > (scrnSize.width - 64.0)) winRect.X = scrnSize.width - 64.0;
if (winRect.Y > (scrnSize.height - 24.0)) winRect.Y = scrnSize.height - 24.0;
winRect.Y -= winRect.H;
winCount = (++winCount) % 5;
/* reposition window */
[paletteWindow placeWindow:&winRect];
[self windowDidResize:paletteWindow];
[paletteScroll getContentSize:&clipSize];
windowOverhead.width = winRect.W - clipSize.width ;
windowOverhead.height = winRect.H - clipSize.height;
/* clear window header statistics */
[self cellResignedSelected:(id)nil];
/* register window */
isRegistered = NO;
allowDrop = regWindow;
[self _registerWindow];
[paletteWindow setDelegate:self];
[paletteWindow setMiniwindowIcon:"doc"];
[paletteWindow addToEventMask:NX_MOUSEENTEREDMASK];
/* calculate absolute minimum window size */
minCellSize.width = 70.0;
minCellSize.height = 40.0;
[self getWindowSize:&minWindowSize forCellSize:&minCellSize gap:&gap rows:1 cols:1];
/* load file if specified */
sourceFile = (char*)nil;
if (fileName) {
BOOL isDoc=(index(fileName,'\t')||strcmp(fileEXT(fileName),dotDocEXTENSION))?NO:YES;
if (isDoc) sourceFile = NXCopyStringBuffer(fileName);
[self loadFileList:fileName :isDoc:!isDoc];
}
[self _setWindowTitle:sourceFile];
/* make window key */
[paletteWindow display];
[paletteWindow makeKeyAndOrderFront:(id)nil];
/* initialize expanded view */
[largeView setScrollView:largeScroller];
[largePanel setDelegate:largeView];
return self;
}
/* free image Palette window manager */
- free
{
[paletteWindow free]; // hopefully this takes care of the whole thing
[largePanel free];
if (fileIcon) [fileIcon free];
mutex_free(loadMutex);
free(sourceFile);
return self;
}
// -------------------------------------------------------------------------------------
// active portfolio delegate
/* become active portfolio */
- becomeActivePortfolio:sender
{
isActivePortfolio = YES;
[paletteWindow becomeMainWindow];
return self;
}
/* resign active portfolio */
- resignActivePortfolio:sender
{
isActivePortfolio = NO;
return self;
}
/* return true if active */
- (BOOL)isActivePortfolio
{
return isActivePortfolio;
}
/* make portfolio active */
+ makeActivePortfolio:(Portfolio*)portObj
{
[instanceList makeObjectsPerform:@selector(resignActivePortfolio:) with:(id)nil];
[portObj becomeActivePortfolio:(id)nil];
return self;
}
/* return active portfolio */
+ activePortfolio
{
id pObj = (id)nil;
int i = [instanceList count];
while(i) if ([(pObj = [instanceList objectAt:--i]) isActivePortfolio]) break;
return pObj;
}
// -------------------------------------------------------------------------------------
// document status
/* return true if there are any unsaved files */
+ (BOOL)isDocEdited
{
int i = [instanceList count];
while(i) if ([[instanceList objectAt:--i] isDocEdited]) return YES;
return NO;
}
/* return true if this document has been edited */
- (BOOL)isDocEdited
{
return [paletteWindow isDocEdited];
}
/* return true if loading new images (main thread only!) */
- (BOOL)isLoading
{
return isLOADING;
}
// -------------------------------------------------------------------------------------
// font
/* return matrix font */
- font
{
return [iconMatrix font];
}
/* change font */
- setFont:fontObj
{
[iconMatrix setFont:fontObj];
return self;
}
// -------------------------------------------------------------------------------------
// button methods
/* show Palette window */
- show:sender
{
[paletteWindow display];
[paletteWindow makeKeyAndOrderFront:(id)nil];
return self;
}
/* open file */
- open:sender
{
return self;
}
/* save file names (called by first responder) */
- save:sender
{
/* get file name to save */
if (!sourceFile) {
const char *temp;
id pSave = [self _savePanel:"Save Image Palette" :docEXTENSION];
if (![pSave runModalForDirectory:[NXApp lastPath] file:""]) return self;
if (!(temp=[pSave filename])) return self;
sourceFile = NXCopyStringBuffer(temp);
}
/* open , write data, and close */
[iconMatrix saveToFile:sourceFile];
[self _setWindowTitle:sourceFile];
return self;
}
/* save file names (called by first responder) */
- saveAs:sender
{
if (sourceFile) { free(sourceFile); sourceFile = (char*)nil; }
return [self save:sender];
}
/* save defaults */
- saveDefaults:sender
{
char buff[512];
NXWriteDefault([NXApp appName], dftFormatNAME, [iconMatrix getPreferenceString:buff]);
return self;
}
// -------------------------------------------------------------------------------------
// calculate window size overhead
// - This method needs to know the layout of the screen to properly calculate its size
- getWindowSize:(NXSize*)windowSize forCellSize:(NXSize*)cellSize gap:(NXSize*)gapSize
rows:(int)rows cols:(int)cols
{
NXRect fRect, cRect;
NXSize actualSize;
/* calculate actual size of content area */
actualSize.width = (cellSize->width + gapSize->width ) * cols;
actualSize.height = (cellSize->height + gapSize->height) * rows;
/* calc size of scrollView */
[[paletteScroll class] getFrameSize:&cRect.size forContentSize:&actualSize
horizScroller:([paletteScroll horizScroller]?YES:NO)
vertScroller:([paletteScroll vertScroller]?YES:NO)
borderType:[paletteScroll borderType]];
/* calc size of window contentView */
[windowHeader getFrame:&fRect];
cRect.H += fRect.H;
/* calc window size */
cRect.X = cRect.Y = 0.0;
[[paletteWindow class] getFrameRect:&fRect forContentRect:&cRect
style:[paletteWindow style]];
*windowSize = fRect.size;
return self;
}
/* get current displayed row/col count */
- getDisplayedRows:(int*)rows cols:(int*)cols
{
NXSize scSize, *cellSize = [iconMatrix cellSize], *gap = [iconMatrix intercell];
[paletteScroll getContentSize:&scSize];
*rows = (int)rint(scSize.height / (cellSize->height + gap->height));
*cols = (int)rint(scSize.width / (cellSize->width + gap->width ));
return self;
}
/* return current cell size */
- (NXSize*)cellSize
{
return [iconMatrix cellSize];
}
// -------------------------------------------------------------------------------------
// show selected cell enlarged image (double action from matrix)
/* matrix doubleAction */
- showLargeImage:sender
{
id imageCell = [iconMatrix selectedCell];
/* hide large panel and return if no selected cell */
[largePanel orderOut:self];
if (!imageCell) return self;
/* show enlarged image */
[self showImage:[imageCell image] title:(char*)[imageCell cellTitle]];
return self;
}
/* show specified image */
- showImage:imageId title:(char*)title
{
BOOL hScroll, vScroll;
NXRect cRect, fRect;
static NXSize maxSize, scrSize = { 0.0, 0.0 };
/* init max screen size */
if (!scrSize.width) {
[NXApp getScreenSize:&scrSize];
maxSize.width = floor(scrSize.width * 0.80);
maxSize.height = floor(scrSize.height * 0.80);
}
/* hide large panel and return if no image */
[largePanel orderOut:self];
if (!imageId) return self;
/* set panel image */
[largeView setImage:imageId];
[largePanel setTitle:title];
/* resize panel */
hScroll = vScroll = NO;
[largeView getFrame:&cRect];
[Window getFrameRect:&fRect forContentRect:&cRect style:[largePanel style]];
if ((fRect.W >= scrSize.width) || (fRect.H >= scrSize.height)) {
fRect.size = maxSize;
[largeView windowWillResize:largePanel toSize:&fRect.size];
[Window getContentRect:&cRect forFrameRect:&fRect style:[largePanel style]];
hScroll = vScroll = YES;
}
[largeScroller setHorizScrollerRequired:hScroll];
[largeScroller setVertScrollerRequired:vScroll];
cRect.X = cRect.Y = 0.0;
[largeScroller setFrame:&cRect];
[largePanel sizeWindow:cRect.W :cRect.H];
/* show enlarged image */
[largePanel center];
[largePanel display];
[largePanel makeKeyAndOrderFront:self];
return self;
}
// -------------------------------------------------------------------------------------
// pass file list to workspace manager
/* send files to workspace manager for dragging */
- iconDrag:sender
{
NXRect bFrame, rect;
char *fullPath;
/* ignore if not called by proper button */
if (sender != iconButtonId) return self; // ignore any imposters
/* check for STOP button */
if (isLOADING) { abortLoad = YES; return self; } // stop button pressed
/* ignore if no files selected */
if (!(fullPath = [iconMatrix selectedCellPaths])) return self; // ignore if no files
/* drag selected files */
[iconButtonId getBounds:&bFrame];
rect.W = 48.0;
rect.H = 48.0;
rect.X = (bFrame.W - rect.W) / 2.0;
rect.Y = (bFrame.H - rect.H) / 2.0;
[self _unregisterWindow]; // unregister myself
[iconButtonId dragFile:fullPath fromRect:&rect slideBack:YES event:[NXApp currentEvent]];
[self _registerWindow]; // re-register myself
/* free file list */
free(fullPath);
return self;
}
// -------------------------------------------------------------------------------------
// PaletteMatrix delegate methods
/* (main thread) load tabbed file list */
- loadFileList:(const char*)fileNames :(BOOL)openDoc :(BOOL)chkExtn
{
fileLIST *fl;
/* return if no files to load */
if (!fileNames) return self;
/* allocate and fill file list structure */
fl = (fileLIST*)malloc(sizeof(fileLIST));
fl->list = NXCopyStringBuffer(fileNames);
fl->flags = (chkExtn?2:0) | (openDoc?1:0);
fl->next = (fileLIST*)nil;
/* load images (fork new thread if necessary) */
mutex_lock(loadMutex);
if (loadList) loadList->next = fl; // load already in progress
else {
abortLoad = NO; // reset abort flag
[self _setIcon:stopIcon]; // setup stop button
[iconMatrix clearSelectedCell]; // deselect any selected cells
[self forkPerform:@selector(_doLoad:) with:(id)fl detach:YES];
}
loadList = fl;
mutex_unlock(loadMutex);
return self;
}
/* set image statistics (only called when a cell is (de)selected) */
- clearImageStats:iconImage // 'closedFolder' or 'multiFile' only
{
[iconPathId setStringValue:""];
[iconSizeId setStringValue:""];
[self _setIcon:iconImage];
return self;
}
/* set image statistics */
- setImageStats:imageCell
{
NXRect bBox;
char buff[256];
/* clear stats if no cell */
if (!imageCell) { [self clearImageStats:closedFolder]; return self; }
/* fill info */
[iconPathId setStringValueNoCopy:[imageCell imagePath]];
[[imageCell image] getSize:&bBox.size];
sprintf(buff, "%.0f x %.0f", bBox.W, bBox.H);
[iconSizeId setStringValue:buff];
/* set icon representation */
if (fileIcon) [fileIcon free];
fileIcon = [[Application workspace] getIconForFile:(char*)[imageCell imagePath]];
[self _setIcon:fileIcon];
return self;
}
/* image became selected */
- cellBecameSelected:imageCell
{
[largePanel orderOut:self]; // hide large panel
if ([iconMatrix selectedCellCount] <= 1) [self setImageStats:imageCell];
else { [largeView setImage:(id)nil]; [self clearImageStats:multiFile]; }
return self;
}
/* image became selected */
- cellResignedSelected:imageCell
{
if ([iconMatrix selectedCellCount] > 1) [self clearImageStats:multiFile];
else [self setImageStats:[iconMatrix selectedCell]];
return self;
}
// -------------------------------------------------------------------------------------
// Workspace manager icon dragging support
#define isSHIFT(S) ((([NXApp currentEvent]->flags) & (S))? YES : NO)
static char *wsTiffName = (char*)nil;
/* keyboard shifts states are now current, do load */
- _delayLoad:sender
{
[self loadFileList:wsTiffName:NO:(isSHIFT(NX_CONTROLMASK)?NO:YES)];
return self;
}
/* indicate we accept dragging */
- (BOOL)prepareForDragOperation:sender
{
if (!wsTiffName) return NO;
return YES;
}
/* files dropped */
- (BOOL)performDragOperation:sender
{
[self _setIcon:(id)nil];
if (!wsTiffName) return NO;
[NXApp activateSelf:YES];
[paletteWindow makeKeyAndOrderFront:(id)nil];
[self perform:@selector(_delayLoad:) with:self afterDelay:10 cancelPrevious:NO];
return YES;
}
/* file icon exited */
// - reset the open file folder icon
- draggingExited:sender
{
[self _setIcon:(id)nil];
return self;
}
/* file icon entered */
// - save the list of file names and the representing icon image
- (NXDragOperation)draggingEntered:sender
{
char *fileList;
int fileListLen;
Pasteboard *pb = [sender draggingPasteboard];
if (wsTiffName) { free(wsTiffName); wsTiffName = (char*)nil; }
if ([pb readType:NXFilenamePboardType data:&fileList length:&fileListLen]) {
wsTiffName = NXCopyStringBuffer(fileList);
return NX_DragOperationCopy;
}
return NX_DragOperationNone;
}
/* file icon moved to location */
- (NXDragOperation)draggingUpdated:sender
{
if (!wsTiffName) return NX_DragOperationNone;
[iconButtonId setImage:openFolder];
return NX_DragOperationCopy;
}
/* register window with workspace manager */
- _registerWindow
{
NXAtom pbsTypes[1];
if (!allowDrop || isRegistered) return self;
pbsTypes[0] = NXFilenamePboardType;
[paletteWindow registerForDraggedTypes:pbsTypes count:1];
isRegistered = YES;
return self;
}
/* unregister window with workspace manager */
- _unregisterWindow
{
if (!allowDrop || !isRegistered) return self;
[paletteWindow unregisterDraggedTypes];
isRegistered = NO;
return self;
}
// -------------------------------------------------------------------------------------
// window delegate methods
/* window is resizing */
- windowWillResize:windowId toSize:(NXSize*)newSize
{
float rows, cols, mRow, mCol;
NXSize size, minSize, oneCellSize;
NXSize *cellSize = [iconMatrix cellSize], *gap = [iconMatrix intercell];
/* actual cell size */
oneCellSize = *cellSize;
oneCellSize.width += gap->width ;
oneCellSize.height += gap->height;
/* absolute minimum size */
if (newSize->width < minWindowSize.width ) newSize->width = minWindowSize.width ;
if (newSize->height < minWindowSize.height) newSize->height = minWindowSize.height;
/* allow any window size if Alternate is pressed */
if (isSHIFT(NX_ALTERNATEMASK)) {
if (isLOADING) { // retain original size
NXRect wFrame;
[windowId getFrame:&wFrame];
*newSize = wFrame.size;
return self;
}
[paletteScroll getContentSize:&size];
cols = size.width / oneCellSize.width ;
rows = size.height / oneCellSize.height;
minSize.width = rint((minCellSize.width +gap->width )*cols)+windowOverhead.width ;
minSize.height = rint((minCellSize.height+gap->height)*rows)+windowOverhead.height;
if (newSize->width < minSize.width ) newSize->width = minSize.width;
if (newSize->height < minSize.height) newSize->height = minSize.height;
return self;
}
/* window size must hold at least one cell */
minSize.width = windowOverhead.width + oneCellSize.width ;
minSize.height = windowOverhead.height + oneCellSize.height;
if (newSize->width < minSize.width ) newSize->width = minSize.width ;
if (newSize->height < minSize.height) newSize->height = minSize.height;
/* size window to cell boundary */
cols = rint((newSize->width - windowOverhead.width ) / oneCellSize.width );
rows = rint((newSize->height - windowOverhead.height) / oneCellSize.height);
mCol = ceil((minWindowSize.width - windowOverhead.width ) / oneCellSize.width );
mRow = ceil((minWindowSize.height - windowOverhead.height) / oneCellSize.height);
if (cols < mCol) cols = mCol;
if (rows < mRow) cols = mRow;
newSize->width = windowOverhead.width + cols * oneCellSize.width ;
newSize->height = windowOverhead.height + rows * oneCellSize.height;
return self;
}
/* window is resizing */
- windowDidResize:windowId
{
float oldRows, oldCols;
NXRect sFrame, matFrame, winFrame;
NXSize scSize, *cellSize = [iconMatrix cellSize], *gap = [iconMatrix intercell];
/* ignore if loading is in progress */
if (isSHIFT(NX_ALTERNATEMASK) && isLOADING) return self;
/* get new window size */
[[windowId contentView] getFrame:&winFrame];
/* reposition header */
[windowHeader getFrame:&sFrame];
winFrame.H -= sFrame.H;
sFrame.Y = winFrame.H;
sFrame.W = winFrame.W;
[windowHeader setFrame:&sFrame];
headerSize = sFrame.size;
/* resize iconPathId text field */
[iconPathId getFrame:&sFrame];
sFrame.W = headerSize.width - sFrame.X;
[iconPathId setFrame:&sFrame];
/* save current number of displayed rows/cols */
[paletteScroll getContentSize:&scSize];
oldCols = scSize.width / (cellSize->width + gap->width );
oldRows = scSize.height / (cellSize->height + gap->height);
/* resize scroller */
[paletteScroll getFrame:&sFrame];
sFrame.size = winFrame.size;
sFrame.Y = 0.0;
sFrame.X = 0.0;
[paletteScroll setFrame:&sFrame];
/* resize matrix within scroller */
[paletteScroll getContentSize:&scSize];
[iconMatrix getFrame:&matFrame];
matFrame.size = scSize;
[iconMatrix setFrame:&matFrame];
/* if alternate key is pressed, then resize cells */
if (isSHIFT(NX_ALTERNATEMASK)) {
NXSize size;
size.width = rint(matFrame.W / oldCols) - gap->width;
size.height = rint(matFrame.H / oldRows) - gap->height;
[iconMatrix setCellSize:&size];
}
/* size matrix view to fit cells */
[iconMatrix sizeToCells];
return self;
}
/* window will close */
- windowWillClose:windowId
{
/* delay window close if still loading */
abortLoad = YES;
if (isLOADING) {
[windowId perform:@selector(performClose:) with:self
afterDelay:250 cancelPrevious:NO];
return (id)nil;
}
/* check for saved */
if ([windowId isDocEdited]) {
int rtn;
const char *save, *no, *cncl, *savf;
save = NXLocalizedString("Save", (char*)nil,(char*)nil);
no = NXLocalizedString("No", (char*)nil,(char*)nil);
cncl = NXLocalizedString("Cancel", (char*)nil,(char*)nil);
if (sourceFile) {
char *name = fileNAME(sourceFile);
savf = NXLocalizedString("Save changes to %s?", (char*)nil,(char*)nil);
rtn = NXRunAlertPanel(save, savf, save, no, cncl, name);
free(name);
} else {
savf = NXLocalizedString("Save changes to UNTITLED?", (char*)nil,(char*)nil);
rtn = NXRunAlertPanel(save, savf, save, no, cncl);
}
if (rtn == NX_ALERTOTHER) return (id)nil; // Cancel
if (rtn == NX_ALERTDEFAULT) [self save:(id)nil]; // Save (else No)
}
/* unregister / remove / and free */
[self _unregisterWindow];
[instanceList removeObject:self];
return [NXApp delayedFree:self];
}
/* window became key */
- windowDidBecomeKey:windowId
{
[[self class] makeActivePortfolio:self];
[windowId makeFirstResponder:iconMatrix];
[[FontManager new] setSelFont:[iconMatrix font] isMultiple:NO];
return self;
}
@end